#include <iostream>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <ctime>
#include <sys/wait.h>
#include <sys/types.h>
#include "bolg.hpp"
using namespace std;
enum
{
    ArgcError = 1,
    ArgvError,
    PipeError
};

class Channel
{
public:
    Channel(int wfd,pid_t sub_process_id,string name)
        : wfd_(wfd),sub_process_id_(sub_process_id),name_(name)
        {}
    void printDebug()
    {
        cout<<"name:"<<name_<<endl;
        cout<<"wfd:"<<wfd_<<endl;
        cout<<"pid:"<<sub_process_id_<<endl;
    }
    string getName()
    {
        return name_;
    }
    pid_t getPid()
    {
        return sub_process_id_;
    }
    int getWfd()
    {
        return wfd_;
    }
    void Close()
    {
        close(wfd_);
    }
private:
    int wfd_;
    pid_t sub_process_id_;
    string name_;
};

class ProcessPool
{
public:
    ProcessPool(int sub_process_num)
        : sub_process_num_(sub_process_num)
        {}
    int CreateProcess(work_t work)
    {
        vector<int> fds;
        for(int i = 0;i<sub_process_num_;++i)
        {
            int pipefd[2];
            int n = pipe(pipefd);
            if(n == -1)
            {
                return PipeError;
            }
            pid_t id = fork();
            if(id == 0)
            {
                //子进程，关闭写端
                sleep(2);
                if(!fds.empty())
                {
                    cout<<"close w fd:";
                    for(auto fd:fds)
                    {
                        close(fd);
                        cout<<fd<<" ";
                    }
                    cout<<endl;
                }
                close(pipefd[1]);
                dup2(pipefd[0],0);
                work(pipefd[0]);
                exit(0);

            }
            //父进程,关闭读端
            close(pipefd[0]);
            string cname = "channel-"+to_string(i);
            channels.push_back(Channel(pipefd[1],id,cname));
            fds.push_back(pipefd[1]);
        }
        return 0;
    }
    void KillAllProcess()
    {
        //在回收子进程前，我们需要把pipefd[1]全部关闭
        for(auto&channel:channels)
        {
            channel.Close();
            //关闭完文件描述符后，开始等待。等待进程需要子进程的pid，恰巧我们的Channel中存有子进程的pid
            pid_t pid = channel.getPid();
            pid_t rid = waitpid(pid,nullptr,0);
            if(rid == pid)
            {
                //回收成功
                cout<<"wait sub process:"<<pid<<" success..."<<endl;
            }
            cout<<channel.getName()<<"close done"<<" sub process quit now:"<<channel.getPid()<<endl;
        }
    }
    int NextChannel()
    {
        static int next = 0;
        int c = next;
        next = (next+1)%sub_process_num_;
        return c;
    }
    void SendTaskCode(int index,uint32_t code)
    {
        cout << "send code: " << code << " to " << channels[index].getName() << " sub prorcess id: " << channels[index].getPid() << endl;
        write(channels[index].getPid(), &code, sizeof(code));
    }
    void Debug()
    {
        for(auto&channel:channels)
        {
            channel.printDebug();
            cout<<endl;
        }
    }
private:
   int sub_process_num_;
   vector<Channel> channels;
};


void Usage(const char* tip)
{
    cout<<"Usage:"<<tip<<" sub_process_num"<<endl;
}



void CtrlSubProcess(ProcessPool* processpool,int cnt)
{
    while(cnt--)
    {
        //选择一个进程和通道
        int channel = processpool->NextChannel();
        //选择一个任务
        uint32_t code = NextTask();
        processpool->SendTaskCode(channel,code);
        sleep(1);
    }
}

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        return ArgcError;
    }
    int sub_process_num = stoi(argv[1]);
    if(sub_process_num<1)
    {
        return ArgvError;
    }
    srand((unsigned int)time(nullptr));
    //创建进程池
    ProcessPool* processpool = new ProcessPool(sub_process_num);
    processpool->CreateProcess(worker);
    //processpool->Debug();
    //sleep(2);
    //控制子进程
    CtrlSubProcess(processpool,10);
    //sleep(2);
    //回收子进程
    processpool->KillAllProcess();
    delete processpool;

    return 0;
}